package hyl.base.net.nio;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

import hyl.base.net.IData1;
import hyl.base.net.IpgReader;
import hyl.core.run.IDo1;
import hyl.core.run.MyRun;
import hyl.core.struct.DataQueue;

/*我们假设TCP数据包的结构 包长度+包体 */
/*包长度 = 包体长度,包长度本身是4个字节 可以转换为int类型*/
/*把读到的数据转成 DataPack*/
/**
 * NIOReader 实例化目的 为了接收 socketchannel 中的数据 <br>
 * 
 * 每个通道 channel 都有自己的reader ,reader 把通道中的数据 取出 分解出完整的 数据包NIOData1 <br>
 * 
 * 
 * 
 * 我们可以进行双工通信<br>
 * 
 * 并且解决了分包和粘包的问题<br>
 * 
 * 数据接收后 转交给 上层函数处理<br>
 * 
 * 
 * 区别 bio 包,bio 的客户端非常少, 所以每个客户端都是需要缓存通信数据包的 如果宕机不会丢失数据包,会缓存在队列中,启动后继续服务. 而nio 包
 * 的客户端会很多, 所以不缓存通信数据 DataQueue<NIOData1> _queue 不能缓存数据. 为高性能,如果宕机必然丢失数据包
 * 
 * 
 * @author 37798955@qq.com
 *
 */
public class NIOReader implements IpgReader {

	SocketChannel _当前通道;
	private final DataQueue<NIOData1> _queue = new DataQueue<NIOData1>();
	ByteBuffer readBuffer;
	NIOSocketSrv _srv;
	
	public NIOReader() {
	}
	public SocketChannel getChannel() {
		return _当前通道;
	}
	public ByteBuffer getReadBuf() {
		return readBuffer;
	}

	/* 客户端模式 */
	public NIOReader(SocketChannel sc) {
		// 每个通道有个读写缓存 如果是这样那么
		readBuffer = ByteBuffer.allocate(IData1.BUFSIZE);
		// writeBuffer = ByteBuffer.allocate(READBUFSIZE << 1);
		this._当前通道 = sc;

	}

	IDo1<IpgReader> _收到处理函数 = null;

	/* 服务端和客户端 都适用 */
	public NIOReader(SocketChannel sc, IDo1<IpgReader> 收到处理函数) {
		this(sc);

		if (收到处理函数 != null) {
			this._收到处理函数 = 收到处理函数;
			// 调用线程执行 收到处理函数,执行完毕线程自动关闭
			MyRun.start用户线程(() -> _收到处理函数.run(this));
		}
	}

	// 适用于服务端
	public NIOReader(NIOSocketSrv sk, SocketChannel socket, IDo1<IpgReader> 收到处理函数) {
		this(socket, 收到处理函数);
		_srv = sk;

	}

	// 声明 _当前内容 变量以后, 初始化 数据包,等待写入数据 直到数据包完整
	NIOData1 _当前内容 = new NIOData1();

	/** 每读到一组数据就写入niodata 直到写满（step == 6）,换一个niodata */

	void putData(ByteBuffer buf) {
		// System.out.println("长度="+buf.remaining());
		// int j=0;
		while (buf.hasRemaining()) {
			_当前内容.addData(buf);
			// System.out.println("--");
			if (_当前内容._step == 5) {
				// NIOData1 内容=_当前内容;
				_queue.push(_当前内容);
				// j++;
				_当前内容 = _当前内容.clone();
			}
			// int k = buf.remaining();
			// if (k<10&&k>0)
			// System.out.println(_当前内容.step+"--"+k);//???
			// j++;
			// System.out.println("put:"+j+"长度="+buf.remaining());
		}

	}

	public boolean isConnected() {
		return _当前通道 != null && _当前通道.isConnected();
	}

	/**
	 * bb 必须是 flip() 过的缓存体
	 */
	public boolean send(ByteBuffer bb) {
		if (this._当前通道 == null || bb == null || !bb.hasRemaining())
			return false;
		try {
			this._当前通道.write(bb);
			return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}

	public boolean send(byte[] 数据包) {
		return send(IData1.B类型_数据, 数据包);
	}

	public boolean send(byte 类型, byte[] 数据包) {
		if (this._当前通道 == null || 数据包 == null)
			return false;
		try {
			NIOData1 dp = new NIOData1();
			dp.data = 数据包;
			dp.i类型 = 类型;
			// System.err.println(数据包.length);
			// System.err.println(new String(数据包));
			this._当前通道.write(dp.getSendBytes());
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			close();
			return false;
		}

	}

	public void close() {
		try {
			if (_srv != null)
				_srv.sockmap.remove(_当前通道);
			if (_当前通道 != null) {
				_当前通道.close();
				_当前通道 = null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public NIOData1 pull() {
		return _queue.pollData();

	}

	public byte[] pullData() {
		return _queue.pollData().getData();

	}

	public void pullData(IDo1<byte[]> 回调) {
		while (Thread.currentThread().isAlive()) {
			byte[] data = pullData();
			回调.run(data);
		}
	}

	public void pull(@SuppressWarnings("rawtypes") IDo1<IData1> 回调) {
		while (Thread.currentThread().isAlive()) {
			NIOData1 data = (NIOData1) _queue.pollData();
			回调.run(data);
		}
	}

	@Override
	public SocketAddress get本地连接() {

		try {
			return _当前通道.getLocalAddress();
		} catch (IOException e) {
			return null;
		}
	}

	@Override
	public SocketAddress get远程连接() {

		try {
			return _当前通道.getRemoteAddress();
		} catch (IOException e) {
			return null;
		}
	}

}
